package com.ywk.base.concurrency.threadPool.createWay;

import java.util.concurrent.*;

/**
 * 参考链接：https://blog.csdn.net/lixiangchibang/article/details/110456200
 * @Author zbh
 * @Date 2024/1/3
 */
public class CreateWayTest {

    public static void main(String[] args) {
        createWay();
    }

    static void createWay(){
        /*三种便捷的创建线程池的方式（缺点：使用的是无界的任务队列，容易造成 OOM，线程耗尽，
         应该使用 ThreadPoolExecutor 的构造方法手动指定队列的最大长度）*/
        //方式一：创建固定大小的线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

        //方式二：创建只有一个线程的线程池
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

        //方式三：创建一个不限线程数上限的线程池，任何提交的任务都将立即执行
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

        /*核心方式，上面三种都是基于此实现的【《阿里巴巴Java开发手册》中强制线程池通过 ThreadPoolExecutor 的方式创建】
        corePoolSize：线程池长期维持的线程数，即使线程处于Idle状态，也不会回收。（设置不当会影响效率，甚至耗尽线程）【重点关注】
        maximumPoolSize：线程数的上限（设置不当会影响效率，甚至耗尽线程；）【重点关注】
        keepAliveTime：超过corePoolSize的线程的idle时长,超过这个时间，多余的线程会被回收。
        TimeUnit：keepAliveTime的时间单位
        workQueue：任务的排队队列（设置不当容易导致OOM）【重点关注】
        handler：拒绝策略（设置不当会导致提交任务时抛出异常）【重点关注】*/
        ExecutorService executorService = new ThreadPoolExecutor(
                2,
                2,
                0,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(512), // 使用有界队列，避免OOM
                new ThreadPoolExecutor.DiscardPolicy());

        /*
          拒绝策略	                    拒绝行为
        AbortPolicy	            抛出RejectedExecutionException（默认）
        DiscardPolicy	        什么也不做，直接忽略
        DiscardOldestPolicy	    丢弃执行队列中最老的任务，尝试为当前提交的任务腾出位置
        CallerRunsPolicy	    直接由提交任务者执行这个任务
        */

        /*
        向线程池提交的任务有两种：Runnable和 Callable
        三种提交任务的方式：
                  提交方式	                 是否关心返回结果
        Future<T> submit(Callable<T> task)	    是
        void execute(Runnable command)	        否
        Future<?> submit(Runnable task)	        否，虽然返回Future，但是其get()方法总是返回null
        */

        /*
        正确使用线程池方式：
        1.避免使用无界队列：使用 ThreadPoolExecutor 的构造方法手动指定队列的最大长度，不要使用快捷方法创建线程池
        2.明确拒绝任务时的行为：当任务队列占满的时候就需要关注拒绝策略，选择合适的方式
        3.获取处理结果和异常：线程池的处理结果、以及处理过程中的异常都被包装到Future中，并在调用Future.get()方法时获取，执行过程中的异常会被包装成ExecutionException
        */

        //异常处理
        Future<String> future = executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                //假设报错
                if (true) {
                    throw new RuntimeException("exception in call~");// 该异常会在调用Future.get()时传递给调用者
                } else {
                    return "";
                }
            }
        });

        try {
            String e = future.get();
        } catch (InterruptedException ex) {
//            ex.printStackTrace();
        } catch (ExecutionException ex) {
            ex.printStackTrace();
        }
    }
}
